home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / lysrc.zip / LEXRULES.PAS < prev    next >
Pascal/Delphi Source File  |  1993-01-24  |  18KB  |  590 lines

  1.  
  2. unit LexRules;
  3.  
  4. (* 2-10-91 AG *)
  5.  
  6. (* Copyright (c) 1990,91 by Albert Graef, Schillerstr. 18,
  7.    6509 Schornsheim/Germany
  8.    All rights reserved *)
  9.  
  10. interface
  11.  
  12. uses LexBase, LexTables;
  13.  
  14. (* Parser for Lex grammar rules.
  15.  
  16.    This module implements a parser for Lex grammar rules. It should
  17.    probably be reimplemented using Lex and Yacc, but the irregular
  18.    lexical structure of the Lex language makes that rather tedious,
  19.    so I decided to use a conventional recursive-descent-parser
  20.    instead. *)
  21.  
  22. procedure parse_rule ( rule_no : Integer );
  23.   (* rule parser (rule_no=number of parsed rule) *)
  24.  
  25. (* Return values of rule parser: *)
  26.  
  27. var
  28.  
  29. expr, stmt : String;
  30.   (* expression and statement part of rule *)
  31. cf   : Boolean;
  32.   (* caret flag *)
  33. n_st : Integer;
  34.   (* number of start states in prefix *)
  35. st   : array [1..max_states] of Integer;
  36.   (* start states *)
  37. r    : RegExpr;
  38.   (* parsed expression *)
  39.  
  40. implementation
  41.  
  42. uses LexMsgs;
  43.  
  44. (* Scanner routines:
  45.  
  46.    The following routines provide access to the source line and handle
  47.    macro substitutions. To perform macro substitution, an input buffer
  48.    is maintained which contains the rest of the line to be parsed, plus
  49.    any pending macro substitutions. The input buffer is organized as
  50.    a stack onto which null-terminated replacement strings are pushed
  51.    as macro substitutions are processed (the terminating null-character
  52.    is used as an endmarker for macros, in order to keep track of the
  53.    number of pending macro substitutions); characters are popped from the
  54.    stack via calls to the get_char routine.
  55.  
  56.    In order to perform macro substitution, the scanner also has to
  57.    maintain some state information to be able to determine when it
  58.    is scanning quoted characters, strings or character classes (s.t.
  59.    no macro substitution is performed in such cases).
  60.  
  61.    The scanner also keeps track of the current source line position in
  62.    variable act_pos; if there are any macro substitutions on the stack,
  63.    act_pos will point to the position of the original macro call in the
  64.    source line. This is needed to give proper error diagnostics. *)
  65.  
  66. const max_chars = 2048;
  67.  
  68. var
  69.  
  70. act_pos, bufptr : Integer;
  71.   (* current position in source line and input stack pointer *)
  72. buf : array [1..max_chars] of Char;
  73.   (* input buffer *)
  74. str_state, cclass_state, quote_state : Boolean;
  75.   (* state information *)
  76. n_macros : Integer;
  77.   (* number of macros currently on stack *)
  78.  
  79. procedure mark_error ( msg : String; offset : Integer );
  80.   (* mark error position (offset=offset of error position (to the left of
  81.      act_pos) *)
  82.   begin
  83.     if n_macros=0 then
  84.       error(msg, act_pos-offset)
  85.     else
  86.       error(msg+' in regular definition', act_pos)
  87.   end(*mark_error*);
  88.  
  89. procedure put_str(str : String);
  90.   (* push str onto input stack *)
  91.   var i : Integer;
  92.   begin
  93.     inc(bufptr, length(str));
  94.     if bufptr>max_chars then fatal(macro_stack_overflow);
  95.     for i := 1 to length(str) do
  96.       buf[bufptr-i+1] := str[i];
  97.   end(*put_str*);
  98.  
  99. procedure init_scanner;
  100.   (* initialize the scanner *)
  101.   begin
  102.     act_pos := 1; bufptr := 0;
  103.     str_state := false; cclass_state := false; quote_state := false;
  104.     n_macros := 0;
  105.     put_str(line);
  106.   end(*init_scanner*);
  107.  
  108. function act_char : Char;
  109.   (* current character (#0 if none) *)
  110.   function push_macro : Boolean;
  111.     (* check for macro call at current position in input buffer *)
  112.     function scan_macro ( var name : String ) : Boolean;
  113.       var i : Integer;
  114.       begin
  115.         if (bufptr>1) and
  116.            (buf[bufptr]='{') and (buf[bufptr-1] in letters) then
  117.           begin
  118.             name := '{'; i := bufptr-1;
  119.             while (i>0) and (buf[i] in alphanums) do
  120.               begin
  121.                 name := name+buf[i];
  122.                 dec(i);
  123.               end;
  124.             if (i>0) and (buf[i]='}') then
  125.               begin
  126.                 scan_macro := true;
  127.                 name := name+'}';
  128.                 bufptr := i-1;
  129.               end
  130.             else
  131.               begin
  132.                 scan_macro := false;
  133.                 mark_error(syntax_error, -length(name));
  134.                 bufptr := i;
  135.               end
  136.           end
  137.         else
  138.           scan_macro := false
  139.       end(*scan_macro*);
  140.     var name : String;
  141.     begin
  142.       if scan_macro(name) then
  143.         begin
  144.           push_macro := true;
  145.           with sym_table^[key(name, max_keys, lookup, entry)] do
  146.             if sym_type=macro_sym then
  147.               begin
  148.                 put_str(subst^+#0);
  149.                 inc(n_macros);
  150.               end
  151.             else
  152.               mark_error(undefined_symbol, -1)
  153.         end
  154.       else
  155.         push_macro := false
  156.     end(*push_macro*);
  157.   function pop_macro : Boolean;
  158.     (* check for macro endmarker *)
  159.     begin
  160.       if (bufptr>0) and (buf[bufptr]=#0) then
  161.         begin
  162.           dec(bufptr);
  163.           dec(n_macros);
  164.           if n_macros=0 then act_pos := length(line)-bufptr+1;
  165.           pop_macro := true;
  166.         end
  167.       else
  168.         pop_macro := false
  169.     end(*pop_macro*);
  170.   begin
  171.     if not (str_state or cclass_state or quote_state) then
  172.       while push_macro do while pop_macro do ;
  173.     if bufptr=0 then
  174.       act_char := #0
  175.     else
  176.       begin
  177.         while pop_macro do ;
  178.         act_char := buf[bufptr];
  179.       end
  180.   end(*act_char*);
  181.  
  182. procedure get_char;
  183.   (* get next character *)
  184.   begin
  185.     if bufptr>0 then
  186.       begin
  187.         case buf[bufptr] of
  188.           '\' : quote_state := not quote_state;
  189.           '"' : if quote_state then
  190.                   quote_state := false
  191.                 else if not cclass_state then
  192.                   str_state := not str_state;
  193.           '[' : if quote_state then
  194.                   quote_state := false
  195.                 else if not str_state then
  196.                   cclass_state := true;
  197.           ']' : if quote_state then
  198.                   quote_state := false
  199.                 else if not str_state then
  200.                   cclass_state := false;
  201.           else  quote_state := false;
  202.         end;
  203.         dec(bufptr);
  204.         if n_macros=0 then
  205.           act_pos := length(line)-bufptr+1;
  206.       end
  207.   end(*get_char*);
  208.  
  209. (* Semantic routines: *)
  210.  
  211. procedure add_start_state ( symbol : String );
  212.   (* add start state to st array *)
  213.   begin
  214.     with sym_table^[key(symbol, max_keys, lookup, entry)] do
  215.       if sym_type=start_state_sym then
  216.         begin
  217.           if n_st>=max_start_states then exit; { this shouldn't happen }
  218.           inc(n_st);
  219.           st[n_st] := start_state;
  220.         end
  221.       else
  222.         mark_error(undefined_symbol, length(symbol))
  223.   end(*add_start_state*);
  224.  
  225. (* Parser: *)
  226.  
  227. procedure parse_rule ( rule_no : Integer );
  228.  
  229.   procedure rule ( var done : Boolean );
  230.  
  231.     (* parse rule according to syntax:
  232.  
  233.        rule            : start_state_prefix caret
  234.                   expr [ '$' | '/' expr ]
  235.                 ;
  236.  
  237.        start_state_prefix    : /* empty */
  238.                 | '<' start_state_list '>'
  239.                 ;
  240.  
  241.        start_state_list         : ident { ',' ident }
  242.                                 ;
  243.  
  244.        caret            : /* empty */
  245.                 | '^'
  246.                 ;
  247.  
  248.        expr            : term { '|' term }
  249.                 ;
  250.  
  251.        term            : factor { factor }
  252.                 ;
  253.  
  254.        factor            : char
  255.                 | string
  256.                 | cclass
  257.                 | '.'
  258.                 | '(' expr ')'
  259.                 | factor '*'
  260.                 | factor '+'
  261.                 | factor '?'
  262.                 | factor '{' num [ ',' num ] '}'
  263.                 ;
  264.     *)
  265.  
  266.     procedure start_state_prefix ( var done : Boolean );
  267.       procedure start_state_list ( var done : Boolean );
  268.         procedure ident ( var done : Boolean );
  269.           var idstr : String;
  270.           begin(*ident*)
  271.             done := act_char in letters;   if not done then exit;
  272.             idstr := act_char;
  273.             get_char;
  274.             while act_char in alphanums do
  275.               begin
  276.                 idstr := idstr+act_char;
  277.                 get_char;
  278.               end;
  279.             add_start_state(idstr);
  280.           end(*ident*);
  281.         begin(*start_state_list*)
  282.           ident(done);                     if not done then exit;
  283.           while act_char=',' do
  284.             begin
  285.               get_char;
  286.               ident(done);                 if not done then exit;
  287.             end;
  288.         end(*start_state_list*);
  289.       begin(*start_state_prefix*)
  290.         n_st := 0;
  291.         if act_char='<' then
  292.           begin
  293.             get_char;
  294.             start_state_list(done);        if not done then exit;
  295.             if act_char='>' then
  296.               begin
  297.                 done := true;
  298.                 get_char;
  299.               end
  300.             else
  301.               done := false
  302.           end
  303.         else
  304.           done := true
  305.       end(*start_state_prefix*);
  306.     procedure caret( var done : Boolean );
  307.       begin(*caret*)
  308.         done := true;
  309.         cf   := act_char='^';
  310.         if act_char='^' then get_char;
  311.       end(*caret*);
  312.  
  313.   procedure scan_char ( var done : Boolean; var c : Char );
  314.     var
  315.       oct_val : Byte;
  316.       count : Integer;
  317.     begin
  318.       done := true;
  319.       if act_char='\' then
  320.         begin
  321.           get_char;
  322.           case act_char of
  323.             #0  : done := false;
  324.             'n' : begin
  325.                     c := nl;
  326.                     get_char
  327.                   end;
  328.             'r' : begin
  329.                     c := cr;
  330.                     get_char
  331.                   end;
  332.             't' : begin
  333.                     c := tab;
  334.                     get_char
  335.                   end;
  336.             'b' : begin
  337.                     c := bs;
  338.                     get_char
  339.                   end;
  340.             'f' : begin
  341.                     c := ff;
  342.                     get_char
  343.                   end;
  344.             '0'..'7' : begin
  345.                          oct_val := ord(act_char)-ord('0');
  346.                          get_char;
  347.                          count := 1;
  348.                          while ('0'<=act_char) and
  349.                            (act_char<='7') and
  350.                            (count<3) do
  351.                            begin
  352.                              inc(count);
  353.                              oct_val := oct_val*8+ord(act_char)-ord('0');
  354.                              get_char
  355.                            end;
  356.                          c := chr(oct_val);
  357.                        end
  358.             else  begin
  359.                     c := act_char;
  360.                     get_char
  361.                   end
  362.           end
  363.         end
  364.       else
  365.         begin
  366.           c := act_char;
  367.           get_char
  368.         end
  369.     end(*scan_char*);
  370.   procedure scan_str ( var done : Boolean; var str : String );
  371.     var c : Char;
  372.     begin
  373.       str := '';
  374.       get_char;
  375.       while (act_char<>#0) and (act_char<>'"') do
  376.         begin
  377.           scan_char(done, c);        if not done then exit;
  378.           str := str+c;
  379.         end;
  380.       if act_char=#0 then
  381.         done := false
  382.       else
  383.         begin
  384.           get_char;
  385.           done := true;
  386.         end
  387.     end(*scan_str*);
  388.   procedure scan_cclass( var done : Boolean; var cc : CClass );
  389.     (* scan a character class *)
  390.     var
  391.       caret : boolean;
  392.       c, c1 : Char;
  393.     begin
  394.       cc := [];
  395.       get_char;
  396.       if act_char='^' then
  397.         begin
  398.           caret := true;
  399.           get_char;
  400.         end
  401.       else
  402.         caret := false;
  403.       while (act_char<>#0) and (act_char<>']') do
  404.         begin
  405.           scan_char(done, c);              if not done then exit;
  406.           if act_char='-' then
  407.             begin
  408.               get_char;
  409.               if (act_char<>#0) and (act_char<>']') then
  410.                 begin
  411.                   scan_char(done, c1);     if not done then exit;
  412.                   cc := cc+[c..c1];
  413.                 end
  414.               else
  415.                 cc := cc+[c,'-'];
  416.             end
  417.           else
  418.             cc := cc+[c];
  419.         end;
  420.       if act_char=#0 then
  421.         done := false
  422.       else
  423.         begin
  424.           get_char;
  425.           done := true;
  426.         end;
  427.       if caret then cc := [#1..#255]-cc;
  428.     end(*scan_cclass*);
  429.   procedure scan_num( var done : Boolean; var n : Integer );
  430.     var str : String;
  431.     begin
  432.       if act_char in digits then
  433.         begin
  434.           str := act_char;
  435.           get_char;
  436.           while act_char in digits do
  437.             begin
  438.               str := str+act_char;
  439.               get_char;
  440.             end;
  441.           done := isInt(str, n);
  442.         end
  443.       else
  444.         done := false
  445.     end(*scan_num*);
  446.  
  447.     procedure expr ( var done : Boolean; var r : RegExpr );
  448.       procedure term ( var done : Boolean; var r : RegExpr );
  449.         procedure factor ( var done : Boolean; var r : RegExpr );
  450.           var str  : String;
  451.               cc   : CClass;
  452.               c    : Char;
  453.               n, m : Integer;
  454.           begin(*factor*)
  455.             case act_char of
  456.               '"' : begin
  457.                       scan_str(done, str);         if not done then exit;
  458.                       r := strExpr(newStr(str));
  459.                     end;
  460.               '[' : begin
  461.                       scan_cclass(done, cc);       if not done then exit;
  462.                       r := cclassExpr(newCClass(cc));
  463.                     end;
  464.               '.' : begin
  465.                       get_char;
  466.                       r := cclassExpr(newCClass([#1..#255]-[nl]));
  467.                       done := true;
  468.                     end;
  469.               '(' : begin
  470.                       get_char;
  471.                       expr(done, r);               if not done then exit;
  472.                       if act_char=')' then
  473.                         begin
  474.                           get_char;
  475.                           done := true;
  476.                         end
  477.                       else
  478.                         done := false
  479.                     end;
  480.               else  begin
  481.                       scan_char(done, c);          if not done then exit;
  482.                       r := charExpr(c);
  483.                     end;
  484.             end;
  485.             while done and (act_char in ['*','+','?','{']) do
  486.               case act_char of
  487.                 '*' : begin
  488.                         get_char;
  489.                         r := starExpr(r);
  490.                       end;
  491.                 '+' : begin
  492.                         get_char;
  493.                         r := plusExpr(r);
  494.                       end;
  495.                 '?' : begin
  496.                         get_char;
  497.                         r := optExpr(r);
  498.                       end;
  499.                 '{' : begin
  500.                         get_char;
  501.                         scan_num(done, m);         if not done then exit;
  502.                         if act_char=',' then
  503.                           begin
  504.                             get_char;
  505.                             scan_num(done, n);     if not done then exit;
  506.                             r := mnExpr(r, m, n);
  507.                           end
  508.                         else
  509.                           r := mnExpr(r, m, m);
  510.                         if act_char='}' then
  511.                           begin
  512.                             get_char;
  513.                             done := true;
  514.                           end
  515.                         else
  516.                           done := false
  517.                       end;
  518.               end
  519.           end(*factor*);
  520.         const term_delim : CClass = [#0,' ',tab,'$','|',')','/'];
  521.         var r1 : RegExpr;
  522.         begin(*term*)
  523.           if not (act_char in term_delim) then
  524.             begin
  525.               factor(done, r);             if not done then exit;
  526.               while not (act_char in term_delim) do
  527.                 begin
  528.                   factor(done, r1);        if not done then exit;
  529.                   r := catExpr(r, r1);
  530.                 end
  531.             end
  532.           else
  533.             begin
  534.               r := epsExpr;
  535.               done := true;
  536.             end
  537.         end(*term*);
  538.       var r1 : RegExpr;
  539.       begin(*expr*)
  540.         term(done, r);                     if not done then exit;
  541.         while act_char='|' do
  542.           begin
  543.             get_char;
  544.             term(done, r1);                if not done then exit;
  545.             r := altExpr(r, r1);
  546.           end
  547.       end(*expr*);
  548.  
  549.     var r1, r2 : RegExpr;
  550.  
  551.     begin(*rule*)
  552.       start_state_prefix(done);            if not done then exit;
  553.       caret(done);                         if not done then exit;
  554.       expr(done, r1);                      if not done then exit;
  555.       if act_char='$' then
  556.         begin
  557.           r := catExpr(catExpr(r1,
  558.                  markExpr(rule_no, 1)),
  559.                  cclassExpr(newCClass([nl])));
  560.           get_char;
  561.         end
  562.       else if act_char='/' then
  563.         begin
  564.           get_char;
  565.           expr(done, r2);                  if not done then exit;
  566.           r := catExpr(catExpr(r1,
  567.                  markExpr(rule_no, 1)), r2);
  568.         end
  569.       else
  570.         r := catExpr(r1, markExpr(rule_no, 1));
  571.       r := catExpr(r, markExpr(rule_no, 0));
  572.       done := (act_char=#0) or (act_char=' ') or (act_char=tab);
  573.     end(*rule*);
  574.  
  575.   var done : Boolean;
  576.  
  577.   begin(*parse_rule*)
  578.     init_scanner;
  579.     rule(done);
  580.     if done then
  581.       begin
  582.         expr := copy(line, 1, act_pos-1);
  583.         stmt := copy(line, act_pos, length(line));
  584.       end
  585.     else
  586.       mark_error(syntax_error, 0)
  587.   end(*parse_rule*);
  588.  
  589. end(*LexRules*).
  590.